#pragma once
#include "../base/eec/mutex.hpp"
#include "../base/eec/thread.hpp"
#include "../base/eec/autopinter.hpp"

#include <vector>
#include <memory>


namespace base {

template<typename T>
class HDefaultListener {
public:
	inline void on_start(const base::eec::thread::id& _id) {}
	inline void on_stop(const base::eec::thread::id& _id) {}
	inline void on_begin(const base::eec::thread::id& _id, const T& _t) {}
	inline void on_end(const base::eec::thread::id& _id, const T& _t, unsigned int _ret) {}
};

template<typename _T, typename _L, typename _D>
class Task {
public:
    Task(const _T& _t, const _L& _level, const _D& _desc)
        : vl_(_t)
        , level_(_level)
        , desc_(_desc) {}

public:
	Task(Task& _rhs);

public:
	unsigned run() {
		return vl_();
	}
	const _T& vl() const {
		return vl_;
	}
	const _D& desc() const {
		return desc_;
	}
	const _L& level() const {
		return level_;
	}
private:
	_T vl_;
    _L level_;
	_D desc_;
};

template<typename T, typename L, typename D, typename MsgQueue, typename Listener>
class HThreadPool
{
public:
    typedef Task<T, L, D> TaskType;
	typedef Listener ListenerType;
    typedef EEC_STD::function<void(const T& _runner, const L& _level, const D& _desc)> HRunnerAdderType;

public:
	~HThreadPool();
	HThreadPool();

public:
	inline bool initialize(unsigned int _thread_count, const ListenerType& _listener = ListenerType());
	inline void uninitialize(bool _wait_end = true);

public:
    inline bool add_task(const T& _t, const L& _level, const D& _desc);
    inline HRunnerAdderType adder();

private:
    int entry();

private:
	HThreadPool(const HThreadPool& _rhs);
	const HThreadPool& operator=(const HThreadPool& _rhs);

private:
	base::eec::mutex lock_;
	std::vector<base::eec::thread> threads_;

	MsgQueue queue_;
    ListenerType listener_;
};

template<typename T, typename L, typename D, typename Container, typename Listener>
inline HThreadPool<T, L, D, Container, Listener>::HThreadPool()
{

}

template<typename T, typename L, typename D, typename Container, typename Listener>
inline HThreadPool<T, L, D, Container, Listener>::~HThreadPool()
{
	uninitialize();
}

template<typename T, typename L, typename D, typename Container, typename Listener>
inline bool HThreadPool<T, L, D, Container, Listener>::initialize(unsigned int _count, const ListenerType& _listener)
{
	base::eec::lock_guard<base::eec::mutex> l_lock_guard(lock_);
	if (0 != threads_.size()) {
		return false;
	}
	listener_ = _listener;
	for (unsigned int i = 0; i < _count; ++i) {
		threads_.push_back(base::eec::thread(EEC_STD::bind(&HThreadPool<T, L, D, Container, Listener>::entry, this)));
	}
	
	return false;
}

template<typename T, typename L, typename D, typename Container, typename Listener>
int HThreadPool<T, L, D, Container, Listener>::entry()
{
    base::eec::thread::id l_id = base::eec::thread::get_current_id();
    listener_.on_start(l_id);
    base::eec::shared_ptr<TaskType> l_task;
    do {
        if (!queue_.get(l_task)) {
            continue;
        }
        if (NULL == l_task.get()) {
            listener_.on_stop(l_id);
            return 0;
        }
        listener_.on_begin(l_id, *l_task);
        unsigned int l_result = l_task->run();
        listener_.on_end(l_id, *l_task, l_result);
    } while (1);
    return 0;
}

template<typename T, typename L, typename D, typename Container, typename Listener>
inline void HThreadPool<T, L, D, Container, Listener>::uninitialize(bool _wait_end)
{
	base::eec::lock_guard<base::eec::mutex> l_lock_guard(lock_);
	if (0 == threads_.size()) {
		return;
	}

	for (size_t i = 0; i < threads_.size(); ++i) {
		queue_.set(base::eec::shared_ptr<TaskType>(NULL));
	}

    if (!_wait_end) {
        threads_.clear();
        return;
    }

	for (size_t i = 0; i < threads_.size(); ++i) {
		base::eec::thread& l_thread = threads_[i];
		if (l_thread.joinable()) {
			l_thread.join();
		}
	}
	threads_.clear();
}

template<typename T, typename L, typename D, typename Container, typename Listener>
inline bool HThreadPool<T, L, D, Container, Listener>::add_task(const T& _t, const L& _level, const D& _desc)
{
    if (0 == threads_.size()) {
        return false;
    }
    queue_.set(base::eec::shared_ptr<TaskType>(new TaskType(_t, _level, _desc)));
    return true;
}

template<typename T, typename L, typename D, typename Container, typename Listener>
inline typename HThreadPool<T, L, D, Container, Listener>::HRunnerAdderType HThreadPool<T, L, D, Container, Listener>::adder()
{
	return EEC_STD::bind(&HThreadPool<T, L, D, Container, Listener>::add_task, this, EEC_STD::placeholders::_1, EEC_STD::placeholders::_2, EEC_STD::placeholders::_3);
}

} // namespace base
